//#include "stdafx.h"
#include "DsoundOutput.h"

#pragma comment( lib, "libs/dsound/dxguid.lib" )
#pragma comment( lib, "libs/dsound/Dsound.lib" )
#pragma comment( lib, "Winmm.lib" )
#pragma comment( lib, "Ole32.lib" )

#ifndef DEBUG
#ifdef _DEBUG
#define DEBUG
#endif
#endif

#define MY_RELEASE(p)	{if(p){p->Release();p=NULL;}}

//#ifdef DEBUG
//#include <fstream>
//static std::ofstream f("t.wav",std::ios::binary);
//#endif

INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, TCHAR* strDesc, TCHAR* strDrvName,VOID* pContext )
{
	CDsoundOutput* po=(CDsoundOutput*)pContext;
#ifdef UNICODE
	DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,strDesc,-1,NULL,0,NULL,FALSE);
	WideCharToMultiByte (CP_OEMCP,NULL,strDesc,-1,po->m_pszLDevNames[po->m_nLDevs],dwNum,NULL,FALSE);
#else
	strcpy(po->m_pszLDevNames[po->m_nLDevs],strDesc);
#endif
	if(pGUID==NULL)
	{
		po->m_iCurLDev=po->m_nLDevs;
		memcpy(&(po->guidDev[po->m_iCurLDev]),&GUID_NULL,sizeof(GUID));
	}
	else
	{
		memcpy(&(po->guidDev[po->m_nLDevs]),pGUID,sizeof(GUID));
	}
	++po->m_nLDevs;
	return TRUE;
}

CDsoundOutput::CDsoundOutput(void)
	: m_nLDevs(0)
	, m_iCurLDev(0)
	, m_pDS(NULL)
	, m_fInited(FALSE)
	, m_pDsb(NULL)
	, m_pDsb8(NULL)
	, m_fEnable3D(FALSE)
	, m_fIsRun(FALSE)
	, m_dwBufferSize(1024)
	, m_p1(NULL)
	, m_p2(NULL)
	, m_l1(0)
	, m_l2(0)
	, m_fFirst(FALSE)
{
	DWORD dwVersion = GetVersion();
	DWORD dwWindowsMajorVersion =  (DWORD)(LOBYTE(LOWORD(dwVersion)));
	if(dwWindowsMajorVersion>5)
		m_fIsVista = TRUE;
	else
		m_fIsVista = TRUE;
	HRESULT hr=DS_OK;
	for(int i=0;i<16;++i){
		memcpy(&guidDev[i],&GUID_NULL,sizeof(GUID));
		memset(&m_pszLDevNames[i][0],0,128);
	}
	
	hr=DirectSoundEnumerate((LPDSENUMCALLBACK)DSoundEnumCallback,(LPVOID)this);
	
	if(SUCCEEDED(hr))
	{
		m_fInited=true;
	}

	for(int i=0;i<2;++i){
		m_hMainBufferEvent[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
	}
}

CDsoundOutput::~CDsoundOutput(void)
{
	if(m_fInited)
	{
		Stop();
	}
	for(int i=0;i<2;++i){
		if(m_hMainBufferEvent[i])
		{
			CloseHandle(m_hMainBufferEvent[i]);
			m_hMainBufferEvent[i]=NULL;
		}
	}
}

static HWND g_hThisWindow=NULL;

static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM tid)
{
	unsigned long id;
	HWND   thwnd;
	id=GetWindowThreadProcessId(hwnd,NULL);
	if(id==tid)
	{
		while((thwnd=GetParent(hwnd))!=NULL)
			hwnd=thwnd;
		g_hThisWindow=hwnd;
		return FALSE;
	}
	return TRUE;
}

BOOL CDsoundOutput::Init(void)
{
	if(!m_fInited)
		return FALSE;
	HRESULT hr=DS_OK;
	if(IsEqualGUID(guidDev[m_iCurLDev],GUID_NULL))
	{
		hr=DirectSoundCreate8( NULL, &m_pDS, NULL );
	}
	else
	{
		hr=DirectSoundCreate8(&guidDev[m_iCurLDev], &m_pDS, NULL );
	}
	
	DWORD tid=GetCurrentThreadId();
	EnumWindows(EnumWindowsProc,tid);
	if(g_hThisWindow==NULL)
		return FALSE;
	hr=m_pDS->SetCooperativeLevel (g_hThisWindow,DSSCL_PRIORITY);
	g_hThisWindow=NULL;

	if(SUCCEEDED(hr))
	{
		ZeroMemory(&m_dsbdesc,sizeof(m_dsbdesc));
		if(m_fEnable3D)
		{
			//Add 3D buffer creation
		}
		if(!m_fEnable3D)
		{
			m_dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
			m_dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME| DSBCAPS_GLOBALFOCUS|DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_LOCDEFER; 
			m_dsbdesc.dwBufferBytes = 2*m_dwBufferSize;
			m_dsbdesc.lpwfxFormat = (WAVEFORMATEX*)&m_wfx; 

			HRESULT hr = m_pDS->CreateSoundBuffer(&m_dsbdesc, &m_pDsb, NULL);
			if(FAILED(hr))
			{
				return FALSE;
			}
			hr = m_pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &m_pDsb8);
			if(m_pDsb8==NULL)
			{
				return FALSE;
			}
			MY_RELEASE(m_pDsb);

			LPDIRECTSOUNDNOTIFY8 lpDsNotify;
			DSBPOSITIONNOTIFY PositionNotify[2];
			
			if (SUCCEEDED(hr = m_pDsb8->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)&lpDsNotify))) 
			{
				PositionNotify[0].dwOffset = 0;//m_dwBufferSize>>1;
				PositionNotify[0].hEventNotify = m_hMainBufferEvent[0];
				PositionNotify[1].dwOffset = m_dwBufferSize;//m_dwBufferSize+(m_dwBufferSize>>1);
				PositionNotify[1].hEventNotify = m_hMainBufferEvent[1];
//#ifdef DEBUG
//			{
//				char t[128]={0};
//				sprintf(t,"Offsets: O1=%i , O2=%i\n",PositionNotify[0].dwOffset,PositionNotify[1].dwOffset);
//				OutputDebugStringA(t);
//			}
//#endif
				hr = lpDsNotify->SetNotificationPositions(2, PositionNotify);
				MY_RELEASE(lpDsNotify);
			}
		}
	}

	if(SUCCEEDED(hr))
	{
		return TRUE;
	}
	return FALSE;
}

void CDsoundOutput::Deinit(void)
{
	if(!m_fInited)
		return;
	if(m_fEnable3D)
	{
		//Add 3D buffer releasing
	}
	if(!m_fEnable3D)
	{
		MY_RELEASE(m_pDsb8);
	}
	MY_RELEASE(m_pDS);
	for(int i=0;i<2;++i){
		ResetEvent(m_hMainBufferEvent[i]);
	}
}

BOOL CDsoundOutput::SetFormat(WAVEFORMATEXTENSIBLE* pwfx)
{
	if(m_fIsRun||!m_fInited)
		return FALSE;
	memcpy(&m_wfx,pwfx,sizeof(m_wfx));
	if(!Init())
	{
		return FALSE;
	}
	Deinit();
	return TRUE;
}

BOOL CDsoundOutput::SetDevice(int i)
{
	if(m_fIsRun||!m_fInited)
		return FALSE;
	if(i<0||i>m_nLDevs)
		return FALSE;
	m_iCurLDev=i;
	return TRUE;
}

BOOL CDsoundOutput::Start(void)
{
	HRESULT hr;
	if(m_fIsRun||!m_fInited)
		return FALSE;
	if(!Init())
		return FALSE;
	hr=m_pDsb8->Lock (m_dwBufferSize,m_dwBufferSize,&m_p1,&m_l1,&m_p2,&m_l2,0);
	hr=m_pDsb8->SetVolume(DSBVOLUME_MAX);
	m_fFirst=TRUE;
	m_pDsb8->Play (0,0,DSBPLAY_LOOPING);
	m_fIsRun=TRUE;
	return TRUE;
}

void CDsoundOutput::Stop(void)
{
	HRESULT hr;
	if(!m_fIsRun||!m_fInited)
		return;
	if(m_fFirst)
	{
		hr=m_pDsb8->Unlock(m_p1,m_l1,m_p2,m_l2);
	}
	hr=m_pDsb8->Stop();
	m_fIsRun=FALSE;
	Deinit();
}

BOOL CDsoundOutput::SetBufferSize(DWORD sz)
{
	if(sz<DSBSIZE_MIN||sz>(DSBSIZE_MAX/2))
		return FALSE;
	m_dwBufferSize=sz;
	return TRUE;
}

BOOL CDsoundOutput::FillMainBuffer(PBYTE pData, int iBufIndex)
{
	HRESULT hr=S_OK;
	if(!m_fFirst)
	{
		DWORD pp=0,pw=0;
		m_pDsb8->GetCurrentPosition(&pp,&pw);
		if(iBufIndex==1)
		{
			if(pp<m_dwBufferSize){
				m_pDsb8->SetCurrentPosition(m_dwBufferSize+m_dwBufferSize>>1);
			}
			hr=m_pDsb8->Lock (0,m_dwBufferSize,&m_p1,&m_l1,&m_p2,&m_l2,0);
		}
		if(iBufIndex==0)
		{
			if(pp>m_dwBufferSize){
				m_pDsb8->SetCurrentPosition(m_dwBufferSize>>1);
			}
			hr=m_pDsb8->Lock (m_dwBufferSize,m_dwBufferSize,&m_p1,&m_l1,&m_p2,&m_l2,0);
		}
	}

	memcpy(m_p1, pData, m_l1);
//#ifdef DEBUG
//	f.write((char*)m_p1,m_l1);
//#endif
	if(m_l2)
	{
		memcpy(m_p2, &pData[m_l1], m_l2);
//#ifdef DEBUG
//	f.write((char*)m_p2,m_l2);
//#endif
	}

	hr=m_pDsb8->Unlock(m_p1,m_l1,m_p2,m_l2);
	if(m_fFirst)
	{
		hr=m_pDsb8->SetCurrentPosition(m_dwBufferSize);
		m_fFirst=FALSE;
	}

//#ifdef DEBUG
//			{
//				DWORD pp=0,pw=0;
//				m_pDsb8->GetCurrentPosition(&pp,&pw);
//				char t[256]={0};
//				sprintf(t,"DirectSound buffer %i: p1=%8x l1=%i , p2=%8x l2=%i ,pp=%i pw=%i\n",iBufIndex ,m_p1,m_l1,m_p2,m_l2,pp,pw);
//				OutputDebugStringA(t);
//			}
//#endif

	
	return TRUE;
}
